
package org.amos.upms.modules.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.amos.core.basic.annotation.Log;
import org.amos.core.basic.constant.SystemConstant;
import org.amos.core.basic.utils.AmosUtils;
import org.amos.core.basic.utils.JsonUtils;
import org.amos.core.basic.utils.TreeUtils;
import org.amos.core.basic.utils.crud.WrapperBuilder;
import org.amos.core.frame.utils.IdUtils;
import org.amos.security.domain.bo.AuthInfo;
import org.amos.security.utils.UserUtils;
import org.amos.upms.modules.system.dto.MenuListDTO;
import org.amos.upms.modules.system.entity.Menu;
import org.amos.upms.modules.system.entity.TenantPackage;
import org.amos.upms.modules.system.entity.User;
import org.amos.upms.modules.system.mapper.MenuMapper;
import org.amos.upms.modules.system.service.*;
import org.amos.upms.modules.system.vo.MenuTreeVO;
import org.amos.upms.modules.system.vo.MenuVO;
import org.amos.upms.modules.system.vo.MetaVO;
import org.amos.upms.utils.SysTreeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.amos.core.basic.constant.SystemConstant.*;

/**
 * 系统菜单 服务实现类
 *
 * @author CodeGenerator
 * @since 2020-12-20
 */
@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {
    @Autowired
    private IUserService userService;
    @Autowired
    private IUserRoleService userRoleService;
    @Autowired
    private ITenantPackageService tenantPackageService;
    @Autowired
    @Lazy
    private ITenantService tenantService;
    private final SysTreeUtils<Menu> sysTreeUtils;

    @Override
    @Log(value = "[删除菜单数据]")
    public Boolean remove(Set<Long> ids) {
        List<Long> menuIds = new ArrayList<>();
        // 组装子集id
        ids.forEach(x -> {
            List<Long> childrenMenuIds = sysTreeUtils.getChildrenIds(x, new ArrayList<>(), MenuMapper.class);
            menuIds.addAll(childrenMenuIds);
        });
        // 合并
        menuIds.addAll(ids);
        UpdateWrapper<Menu> uw = new UpdateWrapper<>();
        uw.in(AmosUtils.toDbField(Menu::getId), menuIds).set(AmosUtils.toDbField(Menu::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_ALREADY);
        super.update(uw);
        return Boolean.TRUE;
    }

    @Override
    @Log(value = "[删除菜单数据]")
    public Long saveOrUpdateMenu(Menu menu) {
        Long id = menu.getId();
        if (Objects.nonNull(id)) {
            baseMapper.updateById(menu);
        } else {
            id = IdUtils.nextId();
            menu.setId(id);
            baseMapper.insert(menu);
        }
        return id;
    }

    @Override
    public List<MenuVO> permissions(Set<Long> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return new ArrayList<>();
        }

        List<Menu> allMenus = baseMapper.allMenu();
        List<Menu> roleMenus = baseMapper.getMenusByRoles(roleIds, MENU_TYPE_ALL);
        List<Menu> routes = new LinkedList<>(roleMenus);
        roleMenus.forEach(roleMenu -> generateRoleRoutes(allMenus, routes, roleMenu));
        routes.sort(Comparator.comparing(Menu::getSort));
        return buildMenus(routes);
    }

    @Override
    public List<MenuVO> routes(Set<Long> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return new ArrayList<>();
        }
        //默认查询所有菜单
        List<Menu> menus = baseMapper.allMenu();
        AuthInfo authInfo = UserUtils.getUser();
        boolean administrator = SYS_DEFAULT_ID.equals(authInfo.getTenantId()) && SYS_IS_ADMIN.equals(authInfo.getIsAdmin());
        List<Menu> resultRoutes = new ArrayList<>();
        if (!administrator) {
            List<Menu> allMenus = new LinkedList<>(menus);
            menus = baseMapper.getMenusByRoles(roleIds, MENU_TYPE_ALL);
            List<Menu> routes = new LinkedList<>(menus);
            //完全填充树形结构父子关系
            menus.forEach(menu -> generateRoleRoutes(allMenus, routes, menu));
            resultRoutes.addAll(routes);
        } else {
            resultRoutes.addAll(menus);
        }

        resultRoutes.sort(Comparator.comparing(Menu::getSort));
        return buildMenus(resultRoutes);
    }

    @Override
    public List<MenuVO> selectTree() {
        Long tenantId = tenantService.getTenantId();
        //默认查询所有菜单
        List<Menu> menus = baseMapper.allMenu();
        List<Menu> allMenus = new LinkedList<>(menus);
        List<Menu> roleMenus = Collections.emptyList();
        if (SYS_DEFAULT_ID.equals(tenantId)) {
            roleMenus = allMenus;
        } else {
            roleMenus = baseMapper.getMenusByTenant(tenantId, STS_TENANT_ADMIN_ROLE_ALIAS, null);
        }
        List<Menu> routes = new LinkedList<>(roleMenus);

        //完全填充树形结构父子关系
        roleMenus.forEach(menu -> generateRoleRoutes(allMenus, routes, menu));
        routes.sort(Comparator.comparing(Menu::getSort));
        return buildMenus(routes);
    }

    @Override
    public List<MenuVO> selectTree(MenuListDTO dto) {
        QueryWrapper qw = new WrapperBuilder().build(dto);
        qw.eq(AmosUtils.toDbField(Menu::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_DEFAULT);
        return buildMenus(super.list(qw));
    }

    @Override
    public Set<String> selectUserPermissions(Long userId) {
        List<Menu> userMenu = getUserMenu(userId, SystemConstant.MENU_TYPE_PERMISSION);
        Set<String> permissions = userMenu.stream().map(Menu::getPermission).filter(x -> StrUtil.isNotBlank(x)).collect(Collectors.toSet());
        return permissions;
    }

    @Override
    public List<MenuTreeVO> selectUserTree(Long userId) {
        return buildTree(getUserMenu(userId, SystemConstant.MENU_TYPE_NORMAL));
    }

    @Override
    public List<Long> selectUserTreeID(Long userId) {
        List<Menu> userMenu = getUserMenu(userId, SystemConstant.MENU_TYPE_NORMAL);
        List<Long> ids = userMenu.stream().map(Menu::getId).collect(Collectors.toList());
        return ids;
    }

    @Override
    public List<Long> selectPackageTreeID(Long packageId) {
        TenantPackage tenantPackage = tenantPackageService.getById(packageId);
        List<Long> list = Collections.EMPTY_LIST;
        try {
            list = JsonUtils.jsonToList(tenantPackage.getMenuIds(), Long.class);
        } finally {
            return list;
        }
    }

    @Override
    public List<Long> selectRoleTreeID(Long roleId) {
        List<Menu> menusByRoles = baseMapper.getMenusByRoles(Stream.of(roleId).collect(Collectors.toSet()), null);
        List<Long> ids = menusByRoles.stream().map(Menu::getId).collect(Collectors.toList());
        return ids;
    }

    @Override
    public List<MenuVO> buildMenus(List<Menu> menus) {
        if (CollUtil.isEmpty(menus)) {
            return new ArrayList<>();
        }

        List<MenuVO> originalList = menus.stream().map(x -> {
            MenuVO vo = AmosUtils.copy(x, MenuVO.class);
            MetaVO metaVo = AmosUtils.copy(x, MetaVO.class);
            vo.setMeta(metaVo);
            return vo;
        }).collect(Collectors.toList());
        return TreeUtils.generateTrees(originalList);
    }

    @Override
    public List<MenuTreeVO> buildTree(List<Menu> menus) {
        if (CollUtil.isEmpty(menus)) {
            return new ArrayList<>();
        }
        List<MenuTreeVO> originalList = AmosUtils.listCopy(menus, MenuTreeVO.class);
        return TreeUtils.generateTrees(originalList);
    }

    @Override
    public List<Menu> getParentMenus(Long id, List<Menu> menus) {
        if (ObjectUtil.isEmpty(id)) {
            return menus;
        }
        Menu menu = baseMapper.selectById(id);
        menus.add(menu);
        return getParentMenus(menu.getParentId(), menus);
    }

    @Override
    public List<Menu> getUserMenu(Long userId, Integer menuType) {
        User user = userService.getById(userId);
        if (ObjectUtil.isEmpty(user)) {
            new ArrayList<>();
        }

        Set<Long> roles = userRoleService.findRoleIdsByUserId(userId);
        List<Menu> menusByRoles = baseMapper.getMenusByRoles(roles, menuType);
        return menusByRoles;
    }

    @Override
    public Boolean updateMenu(Set<Long> ids) {
        UpdateWrapper<Menu> uw = new UpdateWrapper<>();
        uw.in(AmosUtils.toDbField(Menu::getId), ids).set(AmosUtils.toDbField(Menu::getIsDeleted), SystemConstant.SYS_DELETE_FLAG_ALREADY);
        this.update(uw);
        return Boolean.TRUE;
    }

    /**
     * @param allMenus
     * @param roleMenus
     * @param route
     */
    public void generateRoleRoutes(List<Menu> allMenus, List<Menu> roleMenus, Menu route) {
        Optional<Menu> menu = allMenus.stream().filter(x -> Objects.equals(x.getId(), route.getParentId())).findFirst();
        if (menu.isPresent() && !roleMenus.contains(menu.get())) {
            roleMenus.add(menu.get());
            generateRoleRoutes(allMenus, roleMenus, menu.get());
        }
    }

}
